package com.bf.electroplating.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bf.electroplating.config.SystemConfig;
import com.bf.electroplating.mapper.PubAttachmentMapper;
import com.bf.electroplating.pojo.entity.PubAttachment;
import com.bf.electroplating.pojo.entity.PubAttachmentRel;
import com.bf.electroplating.pojo.vo.ResultVO;
import com.bf.electroplating.pojo.vo.attachment.AttachmentVo;
import com.bf.electroplating.service.PubAttachmentRelService;
import com.bf.electroplating.service.PubAttachmentService;
import com.bf.electroplating.util.MinioUtils;
import io.minio.GetObjectResponse;
import io.minio.StatObjectResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
* @author 15451
* @description 针对表【pub_attachment(附件表)】的数据库操作Service实现
* @createDate 2025-05-09 11:11:32
*/
@Slf4j
@Service
@Setter( onMethod_ = @Resource)
public class PubAttachmentServiceImpl extends ServiceImpl<PubAttachmentMapper, PubAttachment> implements PubAttachmentService{
    private MinioUtils minioUtils;

    private SystemConfig systemConfig;

    private PubAttachmentRelService iPubAttachmentRelService;
    @Override
    public void download( String id , HttpServletResponse response ) throws Exception{
        PubAttachment att = this.getById(id);
        if (att == null || StringUtils.isBlank(att.getSourceName())) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        // 1) 从 MinIO 取对象并拿到元信息
        try (GetObjectResponse stream = minioUtils.getFileStream(
                systemConfig.getBucketName(),att.getStorePath())) {
            StatObjectResponse stat = minioUtils.getObjectInfo(systemConfig.getBucketName(),att.getStorePath());
            // 2) 设定 HTTP 头
            response.reset();                                  // 清掉可能已有的 header
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition",
                    "attachment; filename=\"" + URLEncoder.encode(stat.object(), StandardCharsets.UTF_8) + "\"");
            response.setHeader("Content-Length", String.valueOf(stat.size()));
            // 3) 把流完整写到客户端
            try (ServletOutputStream os = response.getOutputStream()) {
                stream.transferTo(os);                        // JDK 9+ 写法；Commons-IO 用 IOUtils.copy
                os.flush();                                   // 确保所有字节都到达容器
            }
        }
    }

    @Override
    @Transactional( rollbackFor = Exception.class )
    public ResultVO<AttachmentVo> uploadFile(MultipartFile file ) throws IOException {
        // 文件大小
        long fileSize = file.getSize();
        // 文件类型
        String fileType = file.getContentType();
        String fileName = file.getOriginalFilename();
        if ( StringUtils.isEmpty( fileName ) ) {
            return ResultVO.fail( "文件名不存在" );
        }
        // 文件后缀拓展名
        String extension = fileName.substring( file.getOriginalFilename().lastIndexOf( "." ) );

        LocalDate currentDate = LocalDate.now();
        String dateFolder = currentDate.toString().replace( "-" , "/" ) + "/";
        String filePathName = dateFolder + UUID.randomUUID().toString().replaceAll("-","")+extension;

        // 文件表
        PubAttachment pubAttachment = new PubAttachment();
        pubAttachment.setFileType( fileType );
        pubAttachment.setExrension( extension );
        pubAttachment.setSourceName( fileName );
        pubAttachment.setStorePath( filePathName );
        this.save( pubAttachment );

        // 上传文件
        minioUtils.putObject( systemConfig.getBucketName() , filePathName , file.getInputStream() ,
                fileSize , file.getContentType() );

        AttachmentVo attachmentVo = new AttachmentVo();
        BeanUtils.copyProperties( pubAttachment , attachmentVo );
        attachmentVo.setStorePath( minioUtils.getObjectUrl( systemConfig.getBucketName() , filePathName ).replace(systemConfig.getEndpoint(),systemConfig.getMinioOutUrl()) );
        return ResultVO.ok( attachmentVo );
    }

    @Override
    public ResultVO < String > getFileUrl( String id ) {
        PubAttachment attachment = this.getById( id );
        if ( ObjectUtils.isEmpty( attachment ) || StringUtils.isEmpty( attachment.getStorePath() ) ) {
            return ResultVO.fail( "文件不存在或文件没有文件名" );
        }
        String objectUrl = minioUtils.getObjectUrl( systemConfig.getBucketName() , attachment.getStorePath() );
        return ResultVO.ok( objectUrl , null  );
    }

    @Override
    public AttachmentVo getOneFile( String id , String module ) {
        PubAttachmentRel attachmentRel = iPubAttachmentRelService.getOne( new LambdaQueryWrapper< PubAttachmentRel >()
                        .eq( PubAttachmentRel::getRelationId , id )
                        .eq( PubAttachmentRel::getStorePath , module ) ,
                false );
        if ( ObjectUtils.isEmpty( attachmentRel ) ) {
            return null;
        }
        PubAttachment attachment = this.getById( attachmentRel.getAttId() );
        if ( ObjectUtils.isEmpty( attachment ) ) {
            return null;
        }
        AttachmentVo attachmentVo = new AttachmentVo();
        BeanUtils.copyProperties( attachment , attachmentVo );
        attachmentVo.setStorePath( minioUtils.getObjectUrl( systemConfig.getBucketName() ,
                attachment.getStorePath() ) .replace(systemConfig.getEndpoint(),systemConfig.getMinioOutUrl()));
        return attachmentVo;
    }

    @Override
    public List< AttachmentVo > getWholeFile(String id , String module ) {
        List < PubAttachmentRel > attachmentRelList = iPubAttachmentRelService.list( new LambdaQueryWrapper < PubAttachmentRel >()
                .eq( PubAttachmentRel::getRelationId , id ).eq( PubAttachmentRel::getStorePath , module ) );
        if ( CollectionUtils.isEmpty( attachmentRelList ) ) {
            return new ArrayList<>();
        }
        Map< String,PubAttachment > attachmentMap =
                this.list().stream().collect( Collectors.toMap( PubAttachment::getId , v -> v ) );
        return attachmentRelList.stream().map( v -> {
            PubAttachment attachment = attachmentMap.get( v.getAttId() );
            AttachmentVo attachmentVo = new AttachmentVo();
            BeanUtils.copyProperties( attachment , attachmentVo );
            attachmentVo.setStorePath( minioUtils.getObjectUrl( systemConfig.getBucketName() ,attachment.getStorePath() ).replace(systemConfig.getEndpoint(),systemConfig.getMinioOutUrl()) );

            return attachmentVo;
        } ).collect( Collectors.toList() );
    }

    @Override
    @Transactional ( rollbackFor = Exception.class )
    public void saveOnePubAttachmentRel( AttachmentVo attachmentVo , String id , String module ) {
        if ( org.apache.commons.lang3.ObjectUtils.isNotEmpty( attachmentVo ) ) {
            iPubAttachmentRelService.remove( new LambdaQueryWrapper < PubAttachmentRel >().eq( PubAttachmentRel::getRelationId , id )
                    .eq( PubAttachmentRel::getStorePath , module )
            );
            PubAttachmentRel pubAttachmentRel = new PubAttachmentRel();
            pubAttachmentRel.setAttId( attachmentVo.getId() );
            pubAttachmentRel.setRelationId( id );
            pubAttachmentRel.setStorePath( module );
            iPubAttachmentRelService.save( pubAttachmentRel );
        }
    }

    @Override
    @Transactional ( rollbackFor = Exception.class )
    public void saveWholeAttachmentRel( List < AttachmentVo > attachmentVos , String id , String module ) {
        List < PubAttachmentRel > relList = iPubAttachmentRelService.list( new LambdaQueryWrapper < PubAttachmentRel >()
                .eq( PubAttachmentRel::getRelationId , id )
                .eq( PubAttachmentRel::getStorePath , module )
        );
        iPubAttachmentRelService.remove( new LambdaQueryWrapper < PubAttachmentRel >()
                .eq( PubAttachmentRel::getRelationId , id )
                .eq( PubAttachmentRel::getStorePath , module ) );
        if ( ! CollectionUtils.isEmpty( attachmentVos ) ) {
            List < PubAttachmentRel > pubAttachmentRelList = attachmentVos.stream().map( v -> {
                PubAttachmentRel pubAttachmentRel = new PubAttachmentRel();
                pubAttachmentRel.setAttId( v.getId() );
                pubAttachmentRel.setRelationId( id );
                pubAttachmentRel.setStorePath( module );
                return pubAttachmentRel;
            } ).collect( Collectors.toList() );
            // 找出 relList的 attid 和 attachmentVos的 差集
            List < String > del = relList.stream().map( PubAttachmentRel::getAttId ).collect( Collectors.toList() )
                    .stream().filter( v -> !attachmentVos.stream().map( AttachmentVo::getId ).collect( Collectors.toList() ).contains( v ) ).collect( Collectors.toList() );
            removeByIds( del );
            iPubAttachmentRelService.saveBatch( pubAttachmentRelList );
        }else {
            removeByIds( relList.stream().map( PubAttachmentRel::getAttId ).collect( Collectors.toList() ) );
        }
    }

    @Override
    public List < AttachmentVo > getByIdFile( String id ) {
        List < PubAttachmentRel > attachmentRelList =
                iPubAttachmentRelService.list( new LambdaQueryWrapper < PubAttachmentRel >().eq( PubAttachmentRel::getRelationId , id ));
        if ( CollectionUtils.isEmpty( attachmentRelList ) ) {
            return new ArrayList <>();
        }
        Map < String,PubAttachment > attachmentMap =
                this.list().stream().collect( Collectors.toMap( PubAttachment::getId , v -> v ) );
        return attachmentRelList.stream().map( v -> {
            PubAttachment attachment = attachmentMap.get( v.getAttId() );
            AttachmentVo attachmentVo = new AttachmentVo();
            BeanUtils.copyProperties( attachment , attachmentVo );
            attachmentVo.setStorePath( minioUtils.getObjectUrl( systemConfig.getBucketName() ,
                    attachment.getStorePath() ) .replace(systemConfig.getEndpoint(),systemConfig.getMinioOutUrl()));

            return attachmentVo;
        } ).collect( Collectors.toList() );
    }

    @Override
    public void deleteByModuleId(String id, String code) {
        List < PubAttachmentRel > attachmentRelList = iPubAttachmentRelService.list( new LambdaQueryWrapper < PubAttachmentRel >()
                .eq( PubAttachmentRel::getStorePath , code )
                .eq( PubAttachmentRel::getRelationId , id) );
        if ( !CollectionUtils.isEmpty( attachmentRelList )){
            this.removeByIds( attachmentRelList.stream().map( PubAttachmentRel::getAttId ).collect( Collectors.toList() ) );
            iPubAttachmentRelService.removeByIds( attachmentRelList.stream().map( PubAttachmentRel::getId ).collect( Collectors.toList() ) );
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadFileFromPath(String filePath, String relationId, String module) {
        if (StringUtils.isBlank(filePath)) {
            log.warn("文件路径为空");
            return null;
        }
        
        try {
            java.io.File file = new java.io.File(filePath);
            if (!file.exists()) {
                log.error("文件不存在：{}", filePath);
                return null;
            }
            
            // 获取文件信息
            String fileName = file.getName();
            String extension = "";
            if (fileName.contains(".")) {
                extension = fileName.substring(fileName.lastIndexOf("."));
            }
            
            // 生成MinIO存储路径
            LocalDate currentDate = LocalDate.now();
            String dateFolder = currentDate.toString().replace("-", "/") + "/";
            String filePathName = dateFolder + UUID.randomUUID().toString().replaceAll("-", "") + extension;
            
            // 上传到MinIO
            try (java.io.FileInputStream inputStream = new java.io.FileInputStream(file)) {
                minioUtils.putObject(
                        systemConfig.getBucketName(), 
                        filePathName, 
                        inputStream, 
                        file.length(), 
                        "image/jpeg"); // 默认图片类型
            }
            
            // 保存附件记录
            PubAttachment attachment = new PubAttachment();
            attachment.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            attachment.setSourceName(fileName);
            attachment.setStorePath(filePathName);
            attachment.setExrension(extension);
            attachment.setFileType("image/jpeg"); // 默认为图片
            attachment.setDelFlag("0");
            this.save(attachment);
            
            // 创建关联关系
            PubAttachmentRel rel = new PubAttachmentRel();
            rel.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            rel.setAttId(attachment.getId());
            rel.setRelationId(relationId);
            rel.setStorePath(module);
            rel.setDelFlag("0");
            iPubAttachmentRelService.save(rel);
            
            log.info("文件上传成功：{} -> {}", filePath, filePathName);
            return attachment.getId();
            
        } catch (Exception e) {
            log.error("上传文件失败：{}", filePath, e);
            throw new RuntimeException("上传文件失败", e);
        }
    }

    @Override
    public String uploadFileToTargetMinio(MultipartFile file, String attachmentType, String id) {
        log.info("开始调用远程服务器更新附件，附件ID：{}，文件名：{}", id, file.getOriginalFilename());
        
        try {
            RestTemplate restTemplate = new RestTemplate();
            String url = "http://36.134.118.75:8100/electroplating/test/updateFile";
            
            // 设置请求超时时间
            org.springframework.http.client.SimpleClientHttpRequestFactory factory = new org.springframework.http.client.SimpleClientHttpRequestFactory();
            factory.setConnectTimeout(30000); // 30秒连接超时
            factory.setReadTimeout(60000);    // 60秒读取超时
            restTemplate.setRequestFactory(factory);
            
            // 创建MultiValueMap用于文件上传
            org.springframework.util.LinkedMultiValueMap<String, Object> body = new org.springframework.util.LinkedMultiValueMap<>();
            body.add("id", id);
            body.add("file", file.getResource());
            
            // 设置请求头
            org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
            headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA);
            
            // 创建请求实体
            org.springframework.http.HttpEntity<org.springframework.util.LinkedMultiValueMap<String, Object>> requestEntity = 
                    new org.springframework.http.HttpEntity<>(body, headers);
            
            // 发送POST请求
            org.springframework.http.ResponseEntity<com.bf.electroplating.pojo.vo.ResultVO> response = 
                    restTemplate.postForEntity(url, requestEntity, com.bf.electroplating.pojo.vo.ResultVO.class);
            
            if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
                com.bf.electroplating.pojo.vo.ResultVO result = response.getBody();
                if ("0000".equals(result.getCode())) {
                    log.info("远程附件更新成功，附件ID：{}", id);
                    return "success";
                } else {
                    log.error("远程附件更新失败，附件ID：{}，错误信息：{}", id, result.getMessage());
                    return "failed: " + result.getMessage();
                }
            } else {
                log.error("远程服务器响应异常，状态码：{}", response.getStatusCode());
                return "failed: 远程服务器响应异常";
            }
            
        } catch (org.springframework.web.client.ResourceAccessException e) {
            log.error("连接远程服务器失败，附件ID：{}，错误：{}", id, e.getMessage());
            return "failed: 连接远程服务器失败 - " + e.getMessage();
        } catch (org.springframework.web.client.HttpClientErrorException e) {
            log.error("HTTP客户端错误，附件ID：{}，状态码：{}，错误：{}", id, e.getStatusCode(), e.getMessage());
            return "failed: HTTP客户端错误 - " + e.getMessage();
        } catch (org.springframework.web.client.HttpServerErrorException e) {
            log.error("HTTP服务器错误，附件ID：{}，状态码：{}，错误：{}", id, e.getStatusCode(), e.getMessage());
            return "failed: HTTP服务器错误 - " + e.getMessage();
        } catch (Exception e) {
            log.error("调用远程服务器更新附件失败，附件ID：{}，错误：{}", id, e.getMessage(), e);
            return "failed: 调用远程服务器失败 - " + e.getMessage();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultVO<AttachmentVo> updateAttachment(String id, MultipartFile file) throws IOException {
        log.info("开始更新附件，附件ID：{}，新文件名：{}", id, file.getOriginalFilename());
        
        // 1. 验证附件是否存在
        PubAttachment existingAttachment = this.getById(id);
        if (existingAttachment == null) {
            log.error("附件不存在，ID：{}", id);
            return ResultVO.fail("附件不存在");
        }
        
        // 2. 验证新文件
        if (file == null || file.isEmpty()) {
            log.error("新文件为空");
            return ResultVO.fail("新文件不能为空");
        }
        
        String newFileName = file.getOriginalFilename();
        if (StringUtils.isBlank(newFileName)) {
            log.error("新文件名不能为空");
            return ResultVO.fail("文件名不能为空");
        }
        
        try {
            // 3. 删除旧文件（从MinIO中删除）
            if (StringUtils.isNotBlank(existingAttachment.getStorePath())) {
                try {
                    minioUtils.removeObject(systemConfig.getBucketName(), existingAttachment.getStorePath());
                    log.info("旧文件删除成功：{}", existingAttachment.getStorePath());
                } catch (Exception e) {
                    log.warn("删除旧文件失败，继续执行更新：{}", e.getMessage());
                }
            }
            
            // 4. 上传新文件
            long fileSize = file.getSize();
            String fileType = file.getContentType();
            String extension = newFileName.substring(newFileName.lastIndexOf("."));
            
            // 生成新的存储路径
            LocalDate currentDate = LocalDate.now();
            String dateFolder = currentDate.toString().replace("-", "/") + "/";
            String newFilePathName = dateFolder + UUID.randomUUID().toString().replaceAll("-", "") + extension;
            
            // 上传到MinIO
            minioUtils.putObject(
                    systemConfig.getBucketName(), 
                    newFilePathName, 
                    file.getInputStream(), 
                    fileSize, 
                    fileType
            );
            
            // 5. 更新数据库记录
            existingAttachment.setSourceName(newFileName);
            existingAttachment.setStorePath(newFilePathName);
            existingAttachment.setFileType(fileType);
            existingAttachment.setExrension(extension);
            existingAttachment.setUpdateTime(new Date());
            
            boolean updateResult = this.updateById(existingAttachment);
            if (!updateResult) {
                log.error("更新附件数据库记录失败");
                // 如果数据库更新失败，删除已上传的文件
                try {
                    minioUtils.removeObject(systemConfig.getBucketName(), newFilePathName);
                } catch (Exception e) {
                    log.error("回滚删除新上传文件失败：{}", e.getMessage());
                }
                return ResultVO.fail("更新附件失败");
            }
            
            // 6. 构建返回结果
            AttachmentVo attachmentVo = new AttachmentVo();
            BeanUtils.copyProperties(existingAttachment, attachmentVo);
            attachmentVo.setStorePath(
                    minioUtils.getObjectUrl(systemConfig.getBucketName(), newFilePathName)
                            .replace(systemConfig.getEndpoint(), systemConfig.getMinioOutUrl())
            );
            
            log.info("附件更新成功，ID：{}，新文件名：{}", id, newFileName);
            return ResultVO.ok(attachmentVo);
            
        } catch (Exception e) {
            log.error("更新附件失败，ID：{}，错误：{}", id, e.getMessage(), e);
            return ResultVO.fail("更新附件失败：" + e.getMessage());
        }
    }
}




